//
// This code was created by Jeff Molofee '99
//
// If you've found this code useful, please let me know.
//
// Visit me at www.demonews.com/hosted/nehe
//

#include <math.h>
#include <windows.h>	// Header File For Windows
#include <gl\gl.h>		// Header File For The OpenGL32 Library
#include <gl\glu.h>		// Header File For The GLu32 Library
#include <gl\glaux.h>	// Header File For The GLaux Library
#include <stdio.h>
#include "hash.h"
#include <string.h>
#include <assert.h>

/************ CONSTANTS *************/

#define NUM_COLUMNS 9
#define PI			3.14159265359
#define MOVE_SPEED	0.5
#define TURN_SPEED	1
#define CAM_MOVE_MAX 0.5
#define BULLET_SPEED 2

#define HEAD_SIZE			0.5
#define TORSO_DEPTH			0.5
#define TORSO_HEIGHT		2
#define TORSO_WIDTH			1.2
#define UPPER_ARM_LENGTH	1
#define LOWER_ARM_LENGTH	1
#define UPPER_ARM_WIDTH		0.5
#define LOWER_ARM_WIDTH		0.5
#define UPPER_LEG_WIDTH		0.5
#define UPPER_LEG_LENGTH	1
#define LOWER_LEG_WIDTH		0.5
#define LOWER_LEG_LENGTH	1

#define ROOM_LENGTH		200.0
#define ROOM_WIDTH		50.0
#define ROOM_HEIGHT		15.0

#define X 0
#define Z 1
#define Y 2

#define COLUMN_WIDTH	3

#define NUMDUDES		3  //Includes Neo, Dude 0
#define NEO_DIST		2.5
GLint	CAMERA_DIST		=5*TORSO_HEIGHT;
GLint	CAM_WALL_DIST	=CAMERA_DIST;
GLfloat RAD_CONVERT		=PI/180;
GLfloat DEG_CONVERT     =180/PI;

GLfloat startArray[] = {50,0,100,-40,100,10};  //make sure 2*NUMDUDES<elem

/***************  STRUCTURES  ***************/

typedef struct bullets {
	float x,y,z;
	float dx,dy,dz;
	struct bullets *next;
} bullets;


typedef struct dudestruct{
	int xstart;
	int ystart;
	//dudestruct *partner;
	GLfloat currentobjs[NUMROTS];
	GLfloat targetobjs[NUMROTS];
	GLfloat waysArray[MAXWAYS][NUMROTS];
	GLfloat moveAmount[NUMROTS];
	GLint positive[NUMROTS];
	GLint numways;
	GLfloat movespeed;
	GLfloat initmovespeed;
	GLfloat inverseMoveSpeed;
	GLint wrapToBeginning;
	movefa *currentstate;
	movefa *nextstate;
	movefa *startstate;
	GLint switchnow;
	GLint currentwaypoint;
	GLint done;
	//picture???
};

/******************* GLOBAL VARIABLES *************/

static	HGLRC hRC;		// Permanent Rendering Context
static	HDC hDC;		// Private GDI Device Context

boolean	keys[256];		// Array Used For The Keyboard Routine
boolean	light;			// Lighting ON/OFF
boolean	lp;				// L Pressed?
boolean	fp;				// F Pressed?

GLfloat LightAmbient[]=		{ 0.5f, 0.5f, 0.5f, 1.0f };
GLfloat LightDiffuse[]=		{ 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat LightPosition[]=	{ 0.0f, 0.0f, 2.0f, 1.0f };

GLuint	filter;			// Which Filter To Use
GLuint	texture[3];		// Storage for 3 textures

GLfloat NeoX = 0.0;
GLfloat NeoY = 0.0;
GLfloat NeoZ = ROOM_LENGTH/2;
GLfloat	yrot = 0.0;			// Y Rotation

GLfloat CameraGoalX = 0.0;
GLfloat CameraGoalZ = 0.0;
GLfloat CameraX;
GLfloat CameraZ;
GLfloat CameraY = (2*LOWER_LEG_LENGTH+2*UPPER_LEG_LENGTH+2*TORSO_HEIGHT+2*HEAD_SIZE+1);
GLfloat ViewHeight = (2*LOWER_LEG_LENGTH+2*UPPER_LEG_LENGTH+2*TORSO_HEIGHT);

POINT curMouse;
boolean bulletRight = FALSE;
bullets *bulletList = NULL;
hashentry *bigArray[16];
dudestruct *dudeArray[NUMDUDES];
GLint lastq=0;
FILE *movefile;
FILE *debugfile;
FILE *fafile;
HashTable *movehash;
movefa *firstfa;
int breaknow;
GLfloat column_locs[NUM_COLUMNS*2+2][2];

/************** FUNCTION DEFINITIONS **************/

boolean bulletCollision(bullets *b);
void MoveBullets(void);
void FireBullet(int dudeNum);
GLvoid DrawDude(GLint dudeNum);
GLvoid LoadNextDudeWayPoint(GLint dudeNum);
GLvoid DudeAI(GLint dudeNum);

void SetCameraLocation(void);
void DrawRoom(void);
void DrawNeo(void);
GLvoid LoadNextWayPoint(GLvoid);
GLvoid LoadMoves();

/*************** GL STRUCTURES (POLYS, TEXS, COLORS, ETC) **************/

static GLfloat colors[] = {1.0, 0.2, 0.2, 0.2, 0.2, 1.0, 0.2, 0.2, 1.0, 1.0, 0.2, 0.2, 1.0, 0.2, 0.2, 0.2, 0.2, 1.0, 1.0,0.2,0.2, 0.2,0.2,1.0};
static GLfloat selectcolors[] = {1.0, 0.2, 1.0, 1.0, 1.0, 0.0, 1.0, 0.2, 1.0, 1.0, 1.0, 0.0, 1.0, 0.2, 1.0, 1.0, 1.0, 0.2, 1.0,0.2,1.0, 1.0,1.0,0.0};
static GLint indices[] = {4,5,6,7,1,2,6,5,0,1,5,4,0,3,2,1,0,4,7,3,2,3,7,6};

static GLfloat head_vertices[] = {-HEAD_SIZE,-HEAD_SIZE,-HEAD_SIZE, HEAD_SIZE,-HEAD_SIZE,-HEAD_SIZE, HEAD_SIZE,HEAD_SIZE,-HEAD_SIZE, -HEAD_SIZE,HEAD_SIZE,-HEAD_SIZE, -HEAD_SIZE,-HEAD_SIZE,HEAD_SIZE, HEAD_SIZE,-HEAD_SIZE,HEAD_SIZE, HEAD_SIZE,HEAD_SIZE,HEAD_SIZE, -HEAD_SIZE,HEAD_SIZE,HEAD_SIZE};
static GLfloat torso_vertices[] = {-TORSO_WIDTH,-TORSO_HEIGHT,-TORSO_DEPTH, TORSO_WIDTH,-TORSO_HEIGHT,-TORSO_DEPTH, TORSO_WIDTH,TORSO_HEIGHT,-TORSO_DEPTH, -TORSO_WIDTH,TORSO_HEIGHT,-TORSO_DEPTH, -TORSO_WIDTH,-TORSO_HEIGHT,TORSO_DEPTH, TORSO_WIDTH,-TORSO_HEIGHT,TORSO_DEPTH, TORSO_WIDTH,TORSO_HEIGHT,TORSO_DEPTH, -TORSO_WIDTH,TORSO_HEIGHT,TORSO_DEPTH};
static GLfloat upper_arm_vertices[] = {-UPPER_ARM_WIDTH,-UPPER_ARM_LENGTH,-UPPER_ARM_WIDTH, UPPER_ARM_WIDTH,-UPPER_ARM_LENGTH,-UPPER_ARM_WIDTH, UPPER_ARM_WIDTH,UPPER_ARM_LENGTH,-UPPER_ARM_WIDTH, -UPPER_ARM_WIDTH,UPPER_ARM_LENGTH,-UPPER_ARM_WIDTH, -UPPER_ARM_WIDTH,-UPPER_ARM_LENGTH,UPPER_ARM_WIDTH, UPPER_ARM_WIDTH,-UPPER_ARM_LENGTH,UPPER_ARM_WIDTH, UPPER_ARM_WIDTH,UPPER_ARM_LENGTH,UPPER_ARM_WIDTH, -UPPER_ARM_WIDTH,UPPER_ARM_LENGTH,UPPER_ARM_WIDTH};
static GLfloat lower_arm_vertices[] = {-LOWER_ARM_WIDTH,-LOWER_ARM_LENGTH,-LOWER_ARM_WIDTH, LOWER_ARM_WIDTH,-LOWER_ARM_LENGTH,-LOWER_ARM_WIDTH, LOWER_ARM_WIDTH,LOWER_ARM_LENGTH,-LOWER_ARM_WIDTH, -LOWER_ARM_WIDTH,LOWER_ARM_LENGTH,-LOWER_ARM_WIDTH, -LOWER_ARM_WIDTH,-LOWER_ARM_LENGTH,LOWER_ARM_WIDTH, LOWER_ARM_WIDTH,-LOWER_ARM_LENGTH,LOWER_ARM_WIDTH, LOWER_ARM_WIDTH,LOWER_ARM_LENGTH,LOWER_ARM_WIDTH, -LOWER_ARM_WIDTH,LOWER_ARM_LENGTH,LOWER_ARM_WIDTH};
static GLfloat upper_leg_vertices[] = {-UPPER_LEG_WIDTH,-UPPER_LEG_LENGTH,-UPPER_LEG_WIDTH, UPPER_LEG_WIDTH,-UPPER_LEG_LENGTH,-UPPER_LEG_WIDTH, UPPER_LEG_WIDTH,UPPER_LEG_LENGTH,-UPPER_LEG_WIDTH, -UPPER_LEG_WIDTH,UPPER_LEG_LENGTH,-UPPER_LEG_WIDTH, -UPPER_LEG_WIDTH,-UPPER_LEG_LENGTH,UPPER_LEG_WIDTH, UPPER_LEG_WIDTH,-UPPER_LEG_LENGTH,UPPER_LEG_WIDTH, UPPER_LEG_WIDTH,UPPER_LEG_LENGTH,UPPER_LEG_WIDTH, -UPPER_LEG_WIDTH,UPPER_LEG_LENGTH,UPPER_LEG_WIDTH};
static GLfloat lower_leg_vertices[] = {-LOWER_LEG_WIDTH,-LOWER_LEG_LENGTH,-LOWER_LEG_WIDTH, LOWER_LEG_WIDTH,-LOWER_LEG_LENGTH,-LOWER_LEG_WIDTH, LOWER_LEG_WIDTH,LOWER_LEG_LENGTH,-LOWER_LEG_WIDTH, -LOWER_LEG_WIDTH,LOWER_LEG_LENGTH,-LOWER_LEG_WIDTH, -LOWER_LEG_WIDTH,-LOWER_LEG_LENGTH,LOWER_LEG_WIDTH, LOWER_LEG_WIDTH,-LOWER_LEG_LENGTH,LOWER_LEG_WIDTH, LOWER_LEG_WIDTH,LOWER_LEG_LENGTH,LOWER_LEG_WIDTH, -LOWER_LEG_WIDTH,LOWER_LEG_LENGTH,LOWER_LEG_WIDTH};

static GLfloat column_vertices[] = {-COLUMN_WIDTH,0,-COLUMN_WIDTH, COLUMN_WIDTH,0,-COLUMN_WIDTH, COLUMN_WIDTH,ROOM_HEIGHT,-COLUMN_WIDTH, -COLUMN_WIDTH,ROOM_HEIGHT,-COLUMN_WIDTH, -COLUMN_WIDTH,0,COLUMN_WIDTH, COLUMN_WIDTH,0,COLUMN_WIDTH, COLUMN_WIDTH,ROOM_HEIGHT,COLUMN_WIDTH, -COLUMN_WIDTH,ROOM_HEIGHT,COLUMN_WIDTH};

// Load Bitmaps And Convert To Textures
GLvoid LoadGLTextures()
{
	// Load Texture
	AUX_RGBImageRec *texture1;

	texture1 = auxDIBImageLoad("Data/crate.bmp");
	if (!texture1)
	{
		exit(1);
	}

	// Create Nearest Filtered Texture
	glGenTextures(3, &texture[0]);
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1->sizeX, texture1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1->data);

	// Create Linear Filtered Texture
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1->sizeX, texture1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1->data);

	// Create MipMapped Texture
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, texture1->sizeX, texture1->sizeY, GL_RGB, GL_UNSIGNED_BYTE, texture1->data);
};

GLvoid InitGL(GLsizei Width, GLsizei Height)	// This Will Be Called Right After The GL Window Is Created
{
	int charfromfile;
	char charbuff[255];
	hashentry *tempmoveStruct;
	int k=0;
	movefa *tempfa; 
	int changed;

	//LoadGLTextures();							// Load The Texture(s)
	//glEnable(GL_TEXTURE_2D);					// Enable Texture Mapping

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);		// This Will Clear The Background Color To Black
	glClearDepth(1.0);							// Enables Clearing Of The Depth Buffer
	glDepthFunc(GL_LESS);						// The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST);					// Enables Depth Testing
	glShadeModel(GL_SMOOTH);					// Enables Smooth Color Shading

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();							// Reset The Projection Matrix

	gluPerspective(75.0f,(GLfloat)Width/(GLfloat)Height,0.1f,200.0f);	// Calculate The Aspect Ratio Of The Window

	glMatrixMode(GL_MODELVIEW);

	glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
	glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);
	glEnable(GL_LIGHT1);
	
	for (int j=0;j<NUMDUDES;j++){
		dudeArray[j]= new dudestruct;
		for (int i=0;i<(NUMROTS);i++){
			dudeArray[j]->currentobjs[i]=0.0f;
			dudeArray[j]->targetobjs[i]=0.0f;
			dudeArray[j]->moveAmount[i]=0.0f;
			dudeArray[j]->positive[i]=0;
			dudeArray[j]->currentobjs[i]=0;
		}
		dudeArray[j]->movespeed=0.1f;
		dudeArray[j]->initmovespeed=dudeArray[j]->movespeed;
		dudeArray[j]->numways=0;
		dudeArray[j]->currentwaypoint=0;
		dudeArray[j]->wrapToBeginning=1;
		dudeArray[j]->done=0;
		dudeArray[j]->currentobjs[NUMROTS-4]=startArray[j*2];
		dudeArray[j]->currentobjs[NUMROTS-6]=startArray[j*2 +1];
	}
	//dudeArray[0]->currentobjs[NUMROTS-4]=50.0;
	breaknow=0;
	movefile=fopen("movefile.txt","r");
	fafile=fopen("fafile.txt","r");
	LoadMoves();
	movehash=new HashTable();
	//int baindex=0;
	while ((charfromfile=getc(movefile))!=EOF){
		tempmoveStruct=new hashentry;
		charbuff[0]=charfromfile;
		int i=1;
		while ((charfromfile=getc(movefile))!=' '){
			charbuff[i]=charfromfile;
			i++;
			if (i==254) break;
		}
		charbuff[i]='\0';
		tempmoveStruct->movename=(char *)malloc(sizeof(char)*(i));
		strcpy(tempmoveStruct->movename,charbuff);
		fscanf(movefile,"%d",&(tempmoveStruct->movenumways));
		for (int j=0;j<tempmoveStruct->movenumways;j++){
			for (int k=0;k<NUMROTS;k++){
				fscanf(movefile,"%f ",&((tempmoveStruct->movewaysArray)[j][k]));
			}
		}	
		tempfa=firstfa;
		changed=0;
		while(tempfa!=NULL){
			if (strcmp(tempfa->startpos,tempmoveStruct->movename)==0){
				changed=1;
				tempmoveStruct->posptr=tempfa;
			}
			tempfa=tempfa->next;
		}
		if (!changed) tempmoveStruct->posptr=NULL;
		movehash->Insert(*tempmoveStruct);
	}
	fclose (fafile);
	fclose(movefile);
	movefile=fopen("movefile.txt","a");
	debugfile=fopen("DebugFile.txt","w");
}

// Load moves from disk.  For now, Neo and Dudes get the same move file, but this 
//should change later

GLvoid LoadMoves(){
	int charfromfile;
	char charbuff[255];
	char charbuff2[255];
	char *bstate;
	char *estate;
	char usercommand;
	int interuptable;
	int found;
	hashentry *temphash;
	movefa *tempmovefa;
	movefa *tempstartfa;
	movefa *tempendfa;
	movefa *templastfa;	
	movefa *startmovefa=new movefa;
	
	startmovefa->startpos="start";
	startmovefa->numbranches=0;
	startmovefa->next=NULL;
	
	while ((charfromfile=getc(fafile))!=EOF){
		tempmovefa=startmovefa;
		tempstartfa=NULL;
		tempendfa=NULL;
		templastfa=NULL;
		boolean foundstart=FALSE;
		boolean foundend=FALSE;
		fscanf(fafile,"%s -> %s %c %d",charbuff,charbuff2,&usercommand,&interuptable);
		bstate=(char *)malloc(sizeof(char)*strlen(charbuff)+1);
		estate=(char *)malloc(sizeof(char)*strlen(charbuff2)+1);
		strcpy(bstate,charbuff);
		strcpy(estate,charbuff2);
		while (tempmovefa!=NULL){
			if (strcmp(tempmovefa->startpos,estate)==0){
				foundend=TRUE;
				tempendfa=tempmovefa;
			}
			if (strcmp(tempmovefa->startpos,bstate)==0){
				foundstart=TRUE;
				tempstartfa=tempmovefa;
			}
			templastfa=tempmovefa;
			tempmovefa=tempmovefa->next;
		}
		if (!foundstart){
			tempstartfa=new movefa;
			tempstartfa->numbranches=0;
			tempstartfa->next=NULL;
			tempstartfa->startpos=bstate;
			templastfa->next=tempstartfa;
			templastfa=tempstartfa;
		}
		if (!foundend){
			tempendfa=new movefa;
			tempendfa->numbranches=0;
			tempendfa->next=NULL;
			tempendfa->startpos=estate;
			templastfa->next=tempendfa;
		}
		if (tempstartfa->numbranches<MAXBRANCH) {
			tempstartfa->branchpos[tempstartfa->numbranches]=tempendfa;
			tempstartfa->branchchar[tempstartfa->numbranches]=usercommand;
			tempstartfa->flags[tempstartfa->numbranches]=interuptable;
			(tempstartfa->numbranches)++;
		}
	}
	for (int i=0;i<NUMDUDES;i++) {
		dudeArray[i]->currentstate=startmovefa;
		dudeArray[i]->switchnow = 0;
	}
	firstfa=startmovefa;
	
}


GLvoid ReSizeGLScene(GLsizei Width, GLsizei Height)
{
	if (Height==0)								// Prevent A Divide By Zero If The Window Is Too Small
		Height=1;

	glViewport(0, 0, Width, Height);			// Reset The Current Viewport And Perspective Transformation

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(75.0f,(GLfloat)Width/(GLfloat)Height,0.1f,200.0f);
	glMatrixMode(GL_MODELVIEW);
}

GLvoid DrawGLScene(GLvoid)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear The Screen And The Depth Buffer
	glLoadIdentity();

	NeoX=dudeArray[0]->currentobjs[NUMROTS-6];
	NeoY=dudeArray[0]->currentobjs[NUMROTS-5];
	NeoZ=dudeArray[0]->currentobjs[NUMROTS-4];
	yrot=dudeArray[0]->currentobjs[NUMROTS-2];

	/**************************** CAMERA *************************/

	SetCameraLocation();

	/**************************** ROOM ****************************/

	glVertexPointer(3, GL_FLOAT, 0, column_vertices);
	for (int c1=0; c1 < NUM_COLUMNS+1; c1++) {    // -X columns
		glPushMatrix();
		glTranslatef(column_locs[c1][0],0.0,column_locs[c1][1]);
		glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
		glPopMatrix();
	}
	for (int c2=0; c2 < NUM_COLUMNS+1; c2++) {    // +X columns
		glPushMatrix();
		glTranslatef(column_locs[NUM_COLUMNS+1+c2][0],0.0,column_locs[NUM_COLUMNS+1+c2][1]);
		glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
		glPopMatrix();
	}
	
	/******************** Draw the Walls ************************/
	
	DrawRoom();

	
	/************************ Dude AI **************************/
	for (int j=1;j<NUMDUDES;j++){
		DudeAI(j);
	}
	/********************* Draw Other Dudes *********************/
	for (int i=1;i<NUMDUDES;i++){
		glPushMatrix();
		glTranslatef(dudeArray[i]->currentobjs[NUMROTS-6],
			dudeArray[i]->currentobjs[NUMROTS-5] + UPPER_LEG_LENGTH*2 +
			LOWER_LEG_LENGTH*2+TORSO_HEIGHT,
			dudeArray[i]->currentobjs[NUMROTS-4]);
		DrawDude(i);
		glPopMatrix();
	}
	
	/**************************  NEO  ******************************/
	glPushMatrix();
	glTranslatef(NeoX,NeoY+UPPER_LEG_LENGTH*2+LOWER_LEG_LENGTH*2+TORSO_HEIGHT,NeoZ);
	DrawDude(0);
	glPopMatrix();

	MoveBullets();
}


GLvoid DudeAI(GLint dudeNum){
	GLfloat neoAngle;
	GLfloat angleDiff;
	GLint changed=0;

	if (NeoZ>dudeArray[dudeNum]->currentobjs[NUMROTS-4]){
			neoAngle=DEG_CONVERT*atan((NeoX-dudeArray[dudeNum]->currentobjs[NUMROTS-6])/
				(NeoZ-dudeArray[dudeNum]->currentobjs[NUMROTS-4]));
	}
	else {
		if (NeoZ==dudeArray[dudeNum]->currentobjs[NUMROTS-4]){
			if (NeoX>dudeArray[dudeNum]->currentobjs[NUMROTS-6]){
				neoAngle=90;
			}
			else
				neoAngle=-90;
		}
		else {
			if (NeoX>dudeArray[dudeNum]->currentobjs[NUMROTS-6]) {
				neoAngle=180-
					DEG_CONVERT*atan((-NeoX+dudeArray[dudeNum]->currentobjs[NUMROTS-6])/
				(NeoZ-dudeArray[dudeNum]->currentobjs[NUMROTS-4]));
			}
			else {
				neoAngle=-180-
					DEG_CONVERT*atan((-NeoX+dudeArray[dudeNum]->currentobjs[NUMROTS-6])/
				(NeoZ-dudeArray[dudeNum]->currentobjs[NUMROTS-4]));
			}
		}
	}
	
	angleDiff=neoAngle-dudeArray[dudeNum]->currentobjs[NUMROTS-2];
	if (abs(angleDiff)>180) {
		if (angleDiff<0)
			angleDiff=angleDiff+360;
		else
			angleDiff=angleDiff-360;
	}
	hashentry *temphash;
	if (abs(angleDiff)<-5) {
		if (strcmp(dudeArray[dudeNum]->currentstate->startpos,"walk")!=0) {
			movehash->Find("walk",&temphash);
			changed=1;
		}
	}
	else {
		if (abs(angleDiff)<10) {
			if (angleDiff>0) {
				if (strcmp(dudeArray[dudeNum]->currentstate->startpos,"turn_left_and_walk")!=0) {
					movehash->Find("turn_left_and_walk",&temphash);
					changed=1;
				}
			}
			else {
				if (strcmp(dudeArray[dudeNum]->currentstate->startpos,"turn_right_and_walk")!=0) {
					movehash->Find("turn_right_and_walk",&temphash);
					changed=1;
				}
			}
		}
		else {
			if (angleDiff>0){
				if (strcmp(dudeArray[dudeNum]->currentstate->startpos,"turn_left")!=0) {
					movehash->Find("turn_left",&temphash);
					changed=1;
				}
			}
			else{
				if (strcmp(dudeArray[dudeNum]->currentstate->startpos,"turn_right")!=0) {
					movehash->Find("turn_right",&temphash);
					changed=1;
				}
			}
		}
	}

	if (changed){
		dudeArray[dudeNum]->switchnow=1;
		for (int k=0;k<temphash->movenumways;k++){
			for (int j=0;j<NUMROTS;j++){
				dudeArray[dudeNum]->waysArray[k][j]=temphash->movewaysArray[k][j];
			}
		}
		dudeArray[dudeNum]->numways=temphash->movenumways;
		dudeArray[dudeNum]->done=0;
		dudeArray[dudeNum]->currentstate=temphash->posptr;
	}
}


/*
struct movefa{
	char *startpos;
	char branchchar[MAXBRANCH];
	movefa *branchpos[MAXBRANCH];
	int flags[MAXBRANCH];
	int numbranches;
	movefa *next;
};
*/



/*
 * this draws the floor, ceiling, and walls
 */
void DrawRoom(void)
{
	glColor3f(0.3f,0.3f,0.3f);		// draw the ceiling
	glBegin(GL_QUADS);									
		glVertex3s(-ROOM_WIDTH,ROOM_HEIGHT,0);
		glVertex3s(-30+COLUMN_WIDTH,ROOM_HEIGHT,0);
		glVertex3s(-30+COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH);
		glVertex3s(-ROOM_WIDTH,ROOM_HEIGHT,ROOM_LENGTH);
	glEnd();						
	glColor3f(0.4f,0.4f,0.4f);		// draw the ceiling
	glBegin(GL_QUADS);
		glVertex3s(-30+COLUMN_WIDTH,3*ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
		glVertex3s(-30+COLUMN_WIDTH,3*ROOM_HEIGHT,0);
		glVertex3s(-30+COLUMN_WIDTH,ROOM_HEIGHT,0);
		glVertex3s(-30+COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
	glEnd();
	glColor3f(0.3f,0.3f,0.3f);		// draw the ceiling
	glBegin(GL_QUADS);									
		glVertex3s(-30+COLUMN_WIDTH,3*ROOM_HEIGHT,0);
		glVertex3s(30-COLUMN_WIDTH,3*ROOM_HEIGHT,0);
		glVertex3s(30-COLUMN_WIDTH,3*ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
		glVertex3s(-30+COLUMN_WIDTH,3*ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
	glEnd();						
	glColor3f(0.4f,0.4f,0.4f);		// draw the ceiling
	glBegin(GL_QUADS);
		glVertex3s(30-COLUMN_WIDTH,3*ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
		glVertex3s(30-COLUMN_WIDTH,3*ROOM_HEIGHT,0);
		glVertex3s(30-COLUMN_WIDTH,ROOM_HEIGHT,0);
		glVertex3s(30-COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
	glEnd();
	glColor3f(0.3f,0.3f,0.3f);		// draw the ceiling
	glBegin(GL_QUADS);									
		glVertex3s(30-COLUMN_WIDTH,ROOM_HEIGHT,0);
		glVertex3s(ROOM_WIDTH,ROOM_HEIGHT,0);
		glVertex3s(ROOM_WIDTH,ROOM_HEIGHT,ROOM_LENGTH);
		glVertex3s(30-COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH);
	glEnd();						
	glColor3f(0.4f,0.4f,0.4f);		// draw the ceiling
	glBegin(GL_QUADS);
		glVertex3s(-30+COLUMN_WIDTH,3*ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
		glVertex3s(30-COLUMN_WIDTH,3*ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
		glVertex3s(30-COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
		glVertex3s(-30+COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
	glEnd();
	glColor3f(0.3f,0.3f,0.3f);		// draw the ceiling
	glBegin(GL_QUADS);
		glVertex3s(-30+COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
		glVertex3s(30-COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH-(20+COLUMN_WIDTH));
		glVertex3s(30-COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH);
		glVertex3s(-30+COLUMN_WIDTH,ROOM_HEIGHT,ROOM_LENGTH);
	glEnd();

	glColor3f(0.7f,0.7f,0.7f);		// draw the floor
	glBegin(GL_QUADS);
		glVertex3s(-ROOM_WIDTH,0,ROOM_LENGTH);
		glVertex3s(ROOM_WIDTH,0,ROOM_LENGTH);
		glVertex3s(ROOM_WIDTH,0,0);
		glVertex3s(-ROOM_WIDTH,0,0);
	glEnd();

	glColor3f(0.5f,0.5f,0.7f);		// draw wall1
	glBegin(GL_QUADS);
		glVertex3s(-ROOM_WIDTH,3*ROOM_HEIGHT,ROOM_LENGTH);
		glVertex3s(ROOM_WIDTH,3*ROOM_HEIGHT,ROOM_LENGTH);
		glVertex3s(ROOM_WIDTH,0,ROOM_LENGTH);
		glVertex3s(-ROOM_WIDTH,0,ROOM_LENGTH);
	glEnd();

	glColor3f(0.5f,0.7f,0.5f);		// draw wall2
	glBegin(GL_QUADS);
		glVertex3s(ROOM_WIDTH,ROOM_HEIGHT,ROOM_LENGTH);
		glVertex3s(ROOM_WIDTH,ROOM_HEIGHT,0);
		glVertex3s(ROOM_WIDTH,0,0);
		glVertex3s(ROOM_WIDTH,0,ROOM_LENGTH);
	glEnd();

	glColor3f(0.7f,0.5f,0.5f);		// draw wall3
	glBegin(GL_QUADS);
		glVertex3s(ROOM_WIDTH,3*ROOM_HEIGHT,0);
		glVertex3s(-ROOM_WIDTH,3*ROOM_HEIGHT,0);
		glVertex3s(-ROOM_WIDTH,0,0);
		glVertex3s(ROOM_WIDTH,0,0);
	glEnd();

	glColor3f(0.5f,0.5f,0.5f);		// draw wall4
	glBegin(GL_QUADS);
		glVertex3s(-ROOM_WIDTH,ROOM_HEIGHT,0);
		glVertex3s(-ROOM_WIDTH,ROOM_HEIGHT,ROOM_LENGTH);
		glVertex3s(-ROOM_WIDTH,0,ROOM_LENGTH);
		glVertex3s(-ROOM_WIDTH,0,0);
	glEnd();
}

GLvoid DrawDude(GLint dudeNum) {

	GLfloat DudeX=dudeArray[dudeNum]->currentobjs[NUMROTS-6];
	GLfloat DudeY=dudeArray[dudeNum]->currentobjs[NUMROTS-5];
	GLfloat DudeZ=dudeArray[dudeNum]->currentobjs[NUMROTS-4];
	glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);

	glRotatef(dudeArray[dudeNum]->currentobjs[NUMROTS-2],0.0f,1.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[NUMROTS-3],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[NUMROTS-1],0.0f,0.0f,1.0f);
	for (int i=0;i<5;i++) 
		glPushMatrix();
	glColorPointer(3, GL_FLOAT, 0, colors);
	glVertexPointer(3, GL_FLOAT, 0, torso_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);
	

	glTranslatef(0,HEAD_SIZE+TORSO_HEIGHT,0);
	
	glVertexPointer(3, GL_FLOAT, 0, head_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);

	glColorPointer(3, GL_FLOAT, 0, colors);
	glPopMatrix();

	glTranslatef(TORSO_WIDTH+UPPER_ARM_WIDTH,TORSO_HEIGHT-UPPER_ARM_WIDTH,0);
	glRotatef(dudeArray[dudeNum]->currentobjs[0],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[1],0.0f,0.0f,1.0f);
	glTranslatef(0,-UPPER_ARM_LENGTH+UPPER_ARM_WIDTH,0);
	glVertexPointer(3, GL_FLOAT, 0, upper_arm_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);

	glTranslatef(0,-(UPPER_ARM_LENGTH+LOWER_ARM_WIDTH),0);
	glRotatef(dudeArray[dudeNum]->currentobjs[2],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[3],0.0f,0.0f,1.0f);
	glTranslatef(0,-LOWER_ARM_LENGTH+LOWER_ARM_WIDTH,0);
	glVertexPointer(3, GL_FLOAT, 0, lower_arm_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);
	glPopMatrix();
	
	glTranslatef(-(TORSO_WIDTH+UPPER_ARM_WIDTH),TORSO_HEIGHT-UPPER_ARM_WIDTH,0);
	glRotatef(dudeArray[dudeNum]->currentobjs[4],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[5],0.0f,0.0f,1.0f);
	glTranslatef(0,-UPPER_ARM_LENGTH+UPPER_ARM_WIDTH,0);
	glVertexPointer(3, GL_FLOAT, 0, upper_arm_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);

	glTranslatef(0,-(UPPER_ARM_LENGTH+LOWER_ARM_WIDTH),0);
	glRotatef(dudeArray[dudeNum]->currentobjs[6],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[7],0.0f,0.0f,1.0f);
	glTranslatef(0,-LOWER_ARM_LENGTH+LOWER_ARM_WIDTH,0);
	glVertexPointer(3, GL_FLOAT, 0, lower_arm_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);
	glPopMatrix();

	
	glTranslatef(TORSO_WIDTH-UPPER_LEG_WIDTH,-(TORSO_HEIGHT+UPPER_LEG_WIDTH),0);
	glRotatef(dudeArray[dudeNum]->currentobjs[8],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[9],0.0f,0.0f,1.0f);
	glTranslatef(0,UPPER_LEG_WIDTH-UPPER_LEG_LENGTH,0);	
	glVertexPointer(3, GL_FLOAT, 0, upper_leg_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);

	glTranslatef(0,-(UPPER_LEG_LENGTH+LOWER_LEG_WIDTH),0);
	glRotatef(dudeArray[dudeNum]->currentobjs[10],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[11],0.0f,0.0f,1.0f);
	glTranslatef(0,-LOWER_LEG_LENGTH+LOWER_LEG_WIDTH,0);
	glVertexPointer(3, GL_FLOAT, 0, lower_leg_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);
	glPopMatrix();

	glTranslatef(-TORSO_WIDTH+UPPER_LEG_WIDTH,-(TORSO_HEIGHT+UPPER_LEG_WIDTH),0);
	glRotatef(dudeArray[dudeNum]->currentobjs[12],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[13],0.0f,0.0f,1.0f);
	glTranslatef(0,UPPER_LEG_WIDTH-UPPER_LEG_LENGTH,0);
	glVertexPointer(3, GL_FLOAT, 0, upper_leg_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);

	glTranslatef(0,-(UPPER_LEG_LENGTH+LOWER_LEG_WIDTH),0);
	glRotatef(dudeArray[dudeNum]->currentobjs[14],1.0f,0.0f,0.0f);
	glRotatef(dudeArray[dudeNum]->currentobjs[15],0.0f,0.0f,1.0f);
	glTranslatef(0,-LOWER_LEG_LENGTH+LOWER_LEG_WIDTH,0);
	glVertexPointer(3, GL_FLOAT, 0, lower_leg_vertices);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
	glColorPointer(3, GL_FLOAT, 0, colors);
	glPopMatrix();



	/*
	 * NUMROTS-1 : xrot
	 * NUMROTS-2 : yrot
	 * NUMROTS-3 : zrot
	 * NUMROTS-4 : Z
	 * NUMROTS-5 : Y
	 * NUMROTS-6 : X
	 */

	int NEO_COLUMN = COLUMN_WIDTH+NEO_DIST;
	int finishedRots=8;
	for (i=0;i<NUMROTS;i++)
	{
		if ((i == 0 || i == 4) && dudeNum == 0) {
			dudeArray[dudeNum]->currentobjs[i] = curMouse.y*180/480 +180;
		}

		else if ((i == 1 || i == 5) && dudeNum == 0) {
			dudeArray[dudeNum]->currentobjs[i] = -curMouse.x*120/640 +60;
		}

		else if (i < 8 && dudeNum == 0) {}

		else if (i != NUMROTS-4 && i != NUMROTS-6)
			if ((dudeArray[dudeNum]->targetobjs[i]>dudeArray[dudeNum]->currentobjs[i] 
				&& dudeArray[dudeNum]->positive[i]) ||
				(dudeArray[dudeNum]->targetobjs[i]<dudeArray[dudeNum]->currentobjs[i] &&
				!dudeArray[dudeNum]->positive[i]))
				dudeArray[dudeNum]->currentobjs[i]+=dudeArray[dudeNum]->moveAmount[i];
			else {
				finishedRots++;
				dudeArray[dudeNum]->currentobjs[i]=dudeArray[dudeNum]->targetobjs[i];
			}
		else if (i == NUMROTS-6) {	// X
			if (dudeArray[dudeNum]->targetobjs[i] < dudeArray[dudeNum]->currentobjs[i] &&
				!dudeArray[dudeNum]->positive[i]) {
				boolean nXmoveOK = TRUE;

				for (int j=0; j < 2*NUM_COLUMNS+2; j++) 
					if ((DudeZ > column_locs[j][1] - NEO_COLUMN) && (DudeZ < column_locs[j][1] + NEO_COLUMN)
						&& (DudeX+dudeArray[dudeNum]->moveAmount[i] < column_locs[j][0] + NEO_COLUMN)
						&& (DudeX+dudeArray[dudeNum]->moveAmount[i] > column_locs[j][0] - NEO_COLUMN))
							nXmoveOK = FALSE;

				if (nXmoveOK && DudeX > -ROOM_WIDTH+NEO_DIST)
					dudeArray[dudeNum]->currentobjs[i]+=dudeArray[dudeNum]->moveAmount[i];
				else {
					finishedRots++;
				}
			}
			else if (dudeArray[dudeNum]->targetobjs[i] > dudeArray[dudeNum]->currentobjs[i] &&
				dudeArray[dudeNum]->positive[i]) {
				boolean pXmoveOK = TRUE;

				for (int j=0; j < 2*NUM_COLUMNS+2; j++) 
					if ((DudeZ > column_locs[j][1] - NEO_COLUMN) && (DudeZ < column_locs[j][1] + NEO_COLUMN)
						&& (DudeX+dudeArray[dudeNum]->moveAmount[i] < column_locs[j][0] + NEO_COLUMN)
						&& (DudeX+dudeArray[dudeNum]->moveAmount[i] > column_locs[j][0] - NEO_COLUMN))
							pXmoveOK = FALSE;

				if (pXmoveOK && DudeX < ROOM_WIDTH-NEO_DIST)
					dudeArray[dudeNum]->currentobjs[i]+=dudeArray[dudeNum]->moveAmount[i];
				else {
					finishedRots++;
				}
			}
			else {
				finishedRots++;
				dudeArray[dudeNum]->currentobjs[i]=dudeArray[dudeNum]->targetobjs[i];
			}
		}
		else {						// Z
			if (dudeArray[dudeNum]->targetobjs[i] < dudeArray[dudeNum]->currentobjs[i] &&
				!dudeArray[dudeNum]->positive[i]) {
				boolean nZmoveOK = TRUE;

				for (int j=0; j < 2*NUM_COLUMNS+2; j++) 
					if ((DudeX > column_locs[j][0] - NEO_COLUMN) && (DudeX < column_locs[j][0] + NEO_COLUMN)
						&& (DudeZ+dudeArray[dudeNum]->moveAmount[i] > column_locs[j][1] - NEO_COLUMN)
						&& (DudeZ+dudeArray[dudeNum]->moveAmount[i] < column_locs[j][1] + NEO_COLUMN))
							nZmoveOK = FALSE;

				if (nZmoveOK && DudeZ > 0+NEO_DIST)
					dudeArray[dudeNum]->currentobjs[i]+=dudeArray[dudeNum]->moveAmount[i];
				else {
					finishedRots++;
				}
			}
			else if (dudeArray[dudeNum]->targetobjs[i] > dudeArray[dudeNum]->currentobjs[i] &&
				dudeArray[dudeNum]->positive[i]) {
				boolean pZmoveOK = TRUE;

				for (int j=0; j < 2*NUM_COLUMNS+2; j++) 
					if ((DudeX > column_locs[j][0] - NEO_COLUMN) && (DudeX < column_locs[j][0] + NEO_COLUMN)
						&& (DudeZ+dudeArray[dudeNum]->moveAmount[i] > column_locs[j][1] - NEO_COLUMN)
						&& (DudeZ+dudeArray[dudeNum]->moveAmount[i] < column_locs[j][1] + NEO_COLUMN))
							pZmoveOK = FALSE;

				if (pZmoveOK && DudeZ < ROOM_LENGTH-NEO_DIST)
					dudeArray[dudeNum]->currentobjs[i]+=dudeArray[dudeNum]->moveAmount[i];
				else {
					finishedRots++;
				}
			}
			else {
				finishedRots++;
				dudeArray[dudeNum]->currentobjs[i]=dudeArray[dudeNum]->targetobjs[i];
			}
		}
	}
	if (finishedRots==NUMROTS || dudeArray[dudeNum]->switchnow){
		dudeArray[dudeNum]->switchnow=0;
		LoadNextDudeWayPoint(dudeNum);
		
	}
}



GLvoid LoadNextDudeWayPoint(GLint dudeNum){
	if (dudeArray[dudeNum]->currentwaypoint<dudeArray[dudeNum]->numways-1 || 
		dudeArray[dudeNum]->wrapToBeginning){
		if (dudeArray[dudeNum]->wrapToBeginning==1 && 
			dudeArray[dudeNum]->currentwaypoint>=(dudeArray[dudeNum]->numways-1)) {
			dudeArray[dudeNum]->currentwaypoint=-1;
			dudeArray[dudeNum]->done=1;
		}
		dudeArray[dudeNum]->currentwaypoint++;
		for (int i=0;i<(NUMROTS);i++){
			if (i<(NUMROTS-6)){
				dudeArray[dudeNum]->targetobjs[i]=
					dudeArray[dudeNum]->waysArray[dudeArray[dudeNum]->currentwaypoint][i];
				dudeArray[dudeNum]->moveAmount[i]=
					(dudeArray[dudeNum]->targetobjs[i]-dudeArray[dudeNum]->currentobjs[i])
					* dudeArray[dudeNum]->movespeed;
			}
			else {
				if (i<NUMROTS-3) {
					if (i==(NUMROTS-6))
						dudeArray[dudeNum]->targetobjs[i]=dudeArray[dudeNum]->currentobjs[i]+
						(dudeArray[dudeNum]->waysArray[dudeArray[dudeNum]->currentwaypoint][NUMROTS-4] *
						sin(dudeArray[dudeNum]->currentobjs[NUMROTS-2] * RAD_CONVERT)) +
						(dudeArray[dudeNum]->waysArray[dudeArray[dudeNum]->currentwaypoint][NUMROTS-6] *
						cos(dudeArray[dudeNum]->currentobjs[NUMROTS-2]* RAD_CONVERT));
					if (i==(NUMROTS-5)) {
						dudeArray[dudeNum]->targetobjs[i]=
							dudeArray[dudeNum]->waysArray[dudeArray[dudeNum]->currentwaypoint][i];
					}
					if (i==(NUMROTS-4)) {
						dudeArray[dudeNum]->targetobjs[i]=dudeArray[dudeNum]->currentobjs[i]+
						(dudeArray[dudeNum]->waysArray[dudeArray[dudeNum]->currentwaypoint][NUMROTS-4] *
						cos(dudeArray[dudeNum]->currentobjs[NUMROTS-2] * RAD_CONVERT)) +
						(dudeArray[dudeNum]->waysArray[dudeArray[dudeNum]->currentwaypoint][NUMROTS-5] *
						sin(dudeArray[dudeNum]->currentobjs[NUMROTS-2] * RAD_CONVERT));
					}
				}
				else {
					if (i==NUMROTS-2){
						dudeArray[dudeNum]->targetobjs[i]=
							dudeArray[dudeNum]->waysArray[dudeArray[dudeNum]->currentwaypoint][i]
							+dudeArray[dudeNum]->currentobjs[i];
					}
					else {
						dudeArray[dudeNum]->targetobjs[i]=
							dudeArray[dudeNum]->waysArray[dudeArray[dudeNum]->currentwaypoint][i];
						if (dudeArray[dudeNum]->currentobjs[i]>=360)
							dudeArray[dudeNum]->currentobjs[i]-=360;
						if (dudeArray[dudeNum]->currentobjs[i]<=-360)
							dudeArray[dudeNum]->currentobjs[i]+=360;
					}	
					
				}
				
				dudeArray[dudeNum]->moveAmount[i]=
					(dudeArray[dudeNum]->targetobjs[i]-dudeArray[dudeNum]->currentobjs[i])
					* dudeArray[dudeNum]->movespeed;
			}
			if (dudeArray[dudeNum]->targetobjs[i]>dudeArray[dudeNum]->currentobjs[i]) 
				dudeArray[dudeNum]->positive[i]=1;
			else
				dudeArray[dudeNum]->positive[i]=0;
		}
	}
}




/* 
 * this determines the location of the camera, given Neo's current
 * X and Z coordinates.  It also handles the possibility of 
 * wall clipping, and insures that it won't occur.
 * problems: follows a linear path from location to goal, not arcing
 */
void SetCameraLocation(void) 
{
	CameraGoalX = NeoX-(CAMERA_DIST * sin((yrot) * RAD_CONVERT));
	CameraGoalZ = NeoZ-(CAMERA_DIST * sin((90-yrot) * RAD_CONVERT));

	/*
	 * other notes:
	 * the current camera float distance is large compared to the distance
	 * between columns.  This needs to be fixed by adjusting the way the camera
	 * floats.  Perhaps adjust the perspective, so that the viewing space
	 * starts later, thus allowing the camera to be within a column, but
	 * still not see it.
	 */

	int NEO_COLUMN = COLUMN_WIDTH+NEO_DIST;
	int CAM_COL_DIST = COLUMN_WIDTH+CAM_WALL_DIST;
	int i;
	for (i=0; (i < NUM_COLUMNS+1) && (NeoX < 0); i++) {
		if ((CameraGoalX > column_locs[i][0] - CAM_COL_DIST) 
			&& (CameraGoalX < column_locs[i][0] + CAM_COL_DIST)
			&& (CameraGoalZ > column_locs[i][1] - CAM_COL_DIST) 
			&& (CameraGoalZ < column_locs[i][1] + CAM_COL_DIST))
		{
			if (NeoX > column_locs[i][0]-10 && NeoX <= column_locs[i][0]-COLUMN_WIDTH)
				CameraGoalX = column_locs[i][0]-CAM_COL_DIST;

			if (NeoX <= column_locs[i][0]+10 && NeoX >= column_locs[i][0]+COLUMN_WIDTH)
				CameraGoalX = column_locs[i][0]+CAM_COL_DIST;

			if (NeoZ >= column_locs[i][1]-10 && NeoZ < column_locs[i][1]
				&& (NeoX > column_locs[i][0]-COLUMN_WIDTH && NeoX < column_locs[i][0]+COLUMN_WIDTH)) {
					CameraGoalZ = column_locs[i][1]-CAM_COL_DIST;
					break;
			}

			if (NeoZ < column_locs[i][1]+10 && NeoZ > column_locs[i][1]
				&& (NeoX > column_locs[i][0]-COLUMN_WIDTH && NeoX < column_locs[i][0]+COLUMN_WIDTH)) {
					CameraGoalZ = column_locs[i][1]+CAM_COL_DIST;
					break;
			}
		}
	}
	for (i=NUM_COLUMNS+1; (i < 2*NUM_COLUMNS+2) && (NeoX > 0); i++) {
		if ((CameraGoalX > column_locs[i][0] - CAM_COL_DIST) 
			&& (CameraGoalX < column_locs[i][0] + CAM_COL_DIST)
			&& (CameraGoalZ > column_locs[i][1] - CAM_COL_DIST) 
			&& (CameraGoalZ < column_locs[i][1] + CAM_COL_DIST))
		{
			if (NeoX < column_locs[i][0]+10 && NeoX >= column_locs[i][0]+COLUMN_WIDTH)
				CameraGoalX = column_locs[i][0]+CAM_COL_DIST;

			if (NeoX >= column_locs[i][0]-10 && NeoX <= column_locs[i][0]-COLUMN_WIDTH)
				CameraGoalX = column_locs[i][0]-CAM_COL_DIST;

			if (NeoZ >= column_locs[i][1]-10 && NeoZ < column_locs[i][1]
				&& (NeoX < column_locs[i][0]+COLUMN_WIDTH && NeoX > column_locs[i][0]-COLUMN_WIDTH)) {
					CameraGoalZ = column_locs[i][1]-CAM_COL_DIST;
					break;
			}

			if (NeoZ < column_locs[i][1]+10 && NeoZ > column_locs[i][1]
				&& (NeoX < column_locs[i][0]+COLUMN_WIDTH && NeoX > column_locs[i][0]-COLUMN_WIDTH)) {
					CameraGoalZ = column_locs[i][1]+CAM_COL_DIST;
					break;
			}
		}
	}

	if (CameraGoalX <= -ROOM_WIDTH+CAM_WALL_DIST && NeoX <= -ROOM_WIDTH+10)
		CameraGoalX = -ROOM_WIDTH+CAM_WALL_DIST;
	if (CameraGoalX >= ROOM_WIDTH-CAM_WALL_DIST && NeoX >= ROOM_WIDTH-10)
		CameraGoalX = ROOM_WIDTH-CAM_WALL_DIST;
	if (CameraGoalZ <= 0+CAM_WALL_DIST && NeoZ <= 0+10)
		CameraGoalZ = 0+CAM_WALL_DIST;
	if (CameraGoalZ >= ROOM_LENGTH-CAM_WALL_DIST && NeoZ >= ROOM_LENGTH-10)
		CameraGoalZ = ROOM_LENGTH-CAM_WALL_DIST;

	/* this should give a neat looking 'slow-camera' type movement
	 * which will lag behind Neo's movements.
	 * unfortunately, it follows a straight line path between the current
	 * camera location and the desired destination, rather than a constant
	 * radius circular arc around Neo.  Which is preferable?
	 */
	GLfloat dCamX = (CameraGoalX - CameraX)/8;
	GLfloat dCamZ = (CameraGoalZ - CameraZ)/8;
	
	if (dCamX > CAM_MOVE_MAX)
		dCamX = CAM_MOVE_MAX;
	if (dCamZ > CAM_MOVE_MAX)
		dCamZ = CAM_MOVE_MAX;

	CameraX += dCamX;
	CameraZ += dCamZ;

	gluLookAt(CameraX,CameraY,CameraZ, NeoX,ViewHeight,NeoZ, 0.0f,1.0f,0.0f);

	/*
	glRotatef(10, 1.0f, 0.0f, 0.0f);
	glTranslatef(0.0,0.0,-(9*TORSO_HEIGHT));
	glRotatef(-yrot, 0.0f,1.0f,0.0f);
	glTranslatef(-NeoX,-(2*LOWER_LEG_LENGTH+2*UPPER_LEG_LENGTH+2*TORSO_HEIGHT+2*HEAD_SIZE+2),NeoZ);
	*/
}

void FireBullet(int dudeNum) {
	bullets *newBullet;
	if (bulletList == NULL) {
		bulletList = new bullets;
		newBullet = bulletList;
	}
	else {
		for (newBullet = bulletList; newBullet->next != NULL; newBullet = newBullet->next);
		newBullet->next = new bullets;
		newBullet = newBullet->next;
	}

	newBullet->next = NULL;

	/* determine bullet position */

	float armLength = 2*UPPER_ARM_LENGTH+2*LOWER_ARM_LENGTH;

	float rx, ry;

	if (bulletRight) {

		rx = dudeArray[dudeNum]->currentobjs[1];
		ry = dudeArray[dudeNum]->currentobjs[0];

		// initial torso offset
		// reverse for L arm 
		newBullet->x = dudeArray[dudeNum]->currentobjs[NUMROTS-6] - (TORSO_WIDTH+UPPER_ARM_WIDTH)*cos(yrot*RAD_CONVERT);
		newBullet->y = dudeArray[dudeNum]->currentobjs[NUMROTS-5] + TORSO_HEIGHT-UPPER_ARM_WIDTH+UPPER_LEG_LENGTH*2+LOWER_LEG_LENGTH*2+TORSO_HEIGHT;
		newBullet->z = dudeArray[dudeNum]->currentobjs[NUMROTS-4] + (TORSO_WIDTH+UPPER_ARM_WIDTH)*sin(yrot*RAD_CONVERT);

	} else {

		rx = dudeArray[dudeNum]->currentobjs[5];
		ry = dudeArray[dudeNum]->currentobjs[4];

		// initial torso offset
		newBullet->x = dudeArray[dudeNum]->currentobjs[NUMROTS-6] + (TORSO_WIDTH+UPPER_ARM_WIDTH)*cos(yrot*RAD_CONVERT);
		newBullet->y = dudeArray[dudeNum]->currentobjs[NUMROTS-5] + TORSO_HEIGHT-UPPER_ARM_WIDTH+UPPER_LEG_LENGTH*2+LOWER_LEG_LENGTH*2+TORSO_HEIGHT;
		newBullet->z = dudeArray[dudeNum]->currentobjs[NUMROTS-4] - (TORSO_WIDTH+UPPER_ARM_WIDTH)*sin(yrot*RAD_CONVERT);

	}
	
	int tempYrot = (int)yrot % 180;
	if (tempYrot < 90)
		newBullet->dx = (1-fabs(tempYrot)/90)*sin((yrot+rx)*RAD_CONVERT) + (fabs(tempYrot)/90)*sin((yrot+rx)*RAD_CONVERT)*fabs(sin(ry*RAD_CONVERT));
	else {
		tempYrot = tempYrot % 90;
		newBullet->dx = (fabs(tempYrot)/90)*sin((yrot+rx)*RAD_CONVERT) + (1-fabs(tempYrot)/90)*sin((yrot+rx)*RAD_CONVERT)*fabs(sin(ry*RAD_CONVERT));
	}

	tempYrot = (int)yrot % 180;
	if (tempYrot < 90)
		newBullet->dz = (fabs(tempYrot)/90)*cos((yrot+rx)*RAD_CONVERT) + (1-fabs(tempYrot)/90)*cos((yrot+rx)*RAD_CONVERT)*fabs(sin(ry*RAD_CONVERT));
	else {
		tempYrot = tempYrot % 90;
		newBullet->dz = (1-fabs(tempYrot)/90)*cos((yrot+rx)*RAD_CONVERT) + (fabs(tempYrot)/90)*cos((yrot+rx)*RAD_CONVERT)*fabs(sin(ry*RAD_CONVERT));
	}
	newBullet->dy = -cos(rx*RAD_CONVERT)*cos(ry*RAD_CONVERT);
	
	newBullet->x += armLength*newBullet->dx;
	newBullet->y += armLength*newBullet->dy;
	newBullet->z += armLength*newBullet->dz;

	bulletRight = !bulletRight;
}

void MoveBullets(void) {
	bullets *a, *b;

	for (a=b=bulletList; b != NULL;) {
		glPushMatrix();
		
		if (bulletCollision(b)) {
			if (b == bulletList) {
				bulletList = b->next;
				a = bulletList;
				delete(b);
				b = a;
			} else {
				a->next = b->next;
				delete(b);
				b = a->next;
			}
			//doDamageToObject();
		}
		else {
			b->x += BULLET_SPEED*b->dx;
			b->y += BULLET_SPEED*b->dy;
			b->z += BULLET_SPEED*b->dz;

			glTranslatef(b->x, b->y, b->z);
	
			glVertexPointer(3, GL_FLOAT, 0, head_vertices);
			glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, indices);
		
			a = b;
			b = b->next;
		}
		glPopMatrix();
	}
}

/* determine if bullet will pass through/into something this round */
boolean bulletCollision(bullets *b) {
	if (b->x < -ROOM_WIDTH || b->x > ROOM_WIDTH || 
		b->z < 0 || b->z > ROOM_LENGTH ||
		b->y < 0 || b->y > 3*ROOM_HEIGHT)
		return true;
	if (b->y > ROOM_HEIGHT && (b->x < -30+COLUMN_WIDTH || b->x > 30-COLUMN_WIDTH))
		return true;


	float m,c;
	for (int i=0; i < 2*NUM_COLUMNS+2; i++) {
		m = b->dz / b->dx;
		c = b->z - b->x * m;
		if (b->z < column_locs[i][1]-COLUMN_WIDTH && b->z + BULLET_SPEED*b->dz >= column_locs[i][1]-COLUMN_WIDTH &&
			(column_locs[i][0]-COLUMN_WIDTH) * m + c > column_locs[i][1]-COLUMN_WIDTH &&
			(column_locs[i][0]+COLUMN_WIDTH) * m + c < column_locs[i][1]-COLUMN_WIDTH)
			return true;
		if (b->z > column_locs[i][1]+COLUMN_WIDTH && b->z + BULLET_SPEED*b->dz <= column_locs[i][1]+COLUMN_WIDTH &&
			(column_locs[i][0]-COLUMN_WIDTH) * m + c < column_locs[i][1]-COLUMN_WIDTH &&
			(column_locs[i][0]+COLUMN_WIDTH) * m + c > column_locs[i][1]-COLUMN_WIDTH)
			return true;
		m = b->dx / b->dz;
		c = b->x - b->z * m;
		if (b->x < column_locs[i][0]-COLUMN_WIDTH && b->x + BULLET_SPEED*b->dx >= column_locs[i][0]-COLUMN_WIDTH &&
			(column_locs[i][1]-COLUMN_WIDTH) * m + c > column_locs[i][0]-COLUMN_WIDTH &&
			(column_locs[i][1]-COLUMN_WIDTH) * m + c < column_locs[i][0]+COLUMN_WIDTH)
			return true;
		if (b->x > column_locs[i][0]+COLUMN_WIDTH && b->x + BULLET_SPEED*b->dx <= column_locs[i][0]+COLUMN_WIDTH &&
			(column_locs[i][1]+COLUMN_WIDTH) * m + c < column_locs[i][0]-COLUMN_WIDTH &&
			(column_locs[i][1]+COLUMN_WIDTH) * m + c > column_locs[i][0]+COLUMN_WIDTH)
			return true;
	}
	
	return false;
}


LRESULT CALLBACK WndProc(	HWND	hWnd,
				UINT	message,
				WPARAM	wParam,
				LPARAM	lParam)
{
	RECT	Screen;							// Used Later On To Get The Size Of The Window
	GLuint	PixelFormat;
	static	PIXELFORMATDESCRIPTOR pfd=
	{
		sizeof(PIXELFORMATDESCRIPTOR),		// Size Of This Pixel Format Descriptor
		1,									// Version Number (?)
		PFD_DRAW_TO_WINDOW |				// Format Must Support Window
		PFD_SUPPORT_OPENGL |				// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,					// Must Support Double Buffering
		PFD_TYPE_RGBA,						// Request An RGBA Format
		16,									// Select A 16Bit Color Depth
		0, 0, 0, 0, 0, 0,					// Color Bits Ignored (?)
		0,									// No Alpha Buffer
		0,									// Shift Bit Ignored (?)
		0,									// No Accumulation Buffer
		0, 0, 0, 0,							// Accumulation Bits Ignored (?)
		16,									// 16Bit Z-Buffer (Depth Buffer)  
		0,									// No Stencil Buffer
		0,									// No Auxiliary Buffer (?)
		PFD_MAIN_PLANE,						// Main Drawing Layer
		0,									// Reserved (?)
		0, 0, 0								// Layer Masks Ignored (?)
	};

	switch (message)						// Tells Windows We Want To Check The Message
	{
		case WM_CREATE:
			hDC = GetDC(hWnd);				// Gets A Device Context For The Window
			PixelFormat = ChoosePixelFormat(hDC, &pfd);		// Finds The Closest Match To The Pixel Format We Set Above

			if (!PixelFormat)
			{
				MessageBox(0,"Can't Find A Suitable PixelFormat.","Error",MB_OK|MB_ICONERROR);
				PostQuitMessage(0);			// This Sends A 'Message' Telling The Program To Quit
				break;						// Prevents The Rest Of The Code From Running
			}

			if(!SetPixelFormat(hDC,PixelFormat,&pfd))
			{
				MessageBox(0,"Can't Set The PixelFormat.","Error",MB_OK|MB_ICONERROR);
				PostQuitMessage(0);
				break;
			}

			hRC = wglCreateContext(hDC);
			if(!hRC)
			{
				MessageBox(0,"Can't Create A GL Rendering Context.","Error",MB_OK|MB_ICONERROR);
				PostQuitMessage(0);
				break;
			}

			if(!wglMakeCurrent(hDC, hRC))
			{
				MessageBox(0,"Can't activate GLRC.","Error",MB_OK|MB_ICONERROR);
				PostQuitMessage(0);
				break;
			}

			GetClientRect(hWnd, &Screen);
			InitGL(Screen.right, Screen.bottom);
			break;

		case WM_DESTROY:
		case WM_CLOSE:
			ChangeDisplaySettings(NULL, 0);

			wglMakeCurrent(hDC,NULL);
			wglDeleteContext(hRC);
			ReleaseDC(hWnd,hDC);

			PostQuitMessage(0);
			break;

		case WM_KEYDOWN:
			keys[wParam] = TRUE;
			break;

		case WM_KEYUP:
			keys[wParam] = FALSE;
			break;

		case WM_SIZE:
			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
			break;

		case WM_LBUTTONDOWN:
			FireBullet(0);
			break;

		case WM_MOUSEMOVE:
			((curMouse).x = ((*((POINTS *)&(lParam)))).x, (curMouse).y = ((*((POINTS *)&(lParam)))).y);
			break;

		default:
			return (DefWindowProc(hWnd, message, wParam, lParam));
	}
return (0);
}


int WINAPI WinMain(	HINSTANCE	hInstance, 
			HINSTANCE	hPrevInstance, 
			LPSTR		lpCmdLine, 
			int			nCmdShow)
{
	MSG			msg;		// Windows Message Structure
	WNDCLASS	wc;			// Windows Class Structure Used To Set Up The Type Of Window
	HWND		hWnd;		// Storage For Window Handle

	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc		= (WNDPROC) WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= hInstance;
	wc.hIcon			= NULL;
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= NULL;
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= "OpenGL WinClass";

	if(!RegisterClass(&wc))
	{
		MessageBox(0,"Failed To Register The Window Class.","Error",MB_OK|MB_ICONERROR);
		return FALSE;
	}

	hWnd = CreateWindow(
	"OpenGL WinClass",
	"The Matrix: The Game",		// Title Appearing At The Top Of The Window

	WS_POPUP |
	WS_CLIPCHILDREN |
	WS_CLIPSIBLINGS,

	0, 0,												// The Position Of The Window On The Screen
	640, 480,											// The Width And Height Of The WIndow

	NULL,
	NULL,
	hInstance,
	NULL);

	if(!hWnd)
	{
		MessageBox(0,"Window Creation Error.","Error",MB_OK|MB_ICONERROR);
		return FALSE;
	}

	DEVMODE dmScreenSettings ;
	dmScreenSettings.dmPelsWidth        = 640;								// Width
	dmScreenSettings.dmPelsHeight       = 480;								// Height
	dmScreenSettings.dmFields           = DM_PELSWIDTH | DM_PELSHEIGHT;		// Color Depth
	ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);				// Switch To Fullscreen Mode

	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
	SetFocus(hWnd);
	wglMakeCurrent(hDC,hRC);


	for (int c1=0; c1 < NUM_COLUMNS; c1++) {
		column_locs[c1][0] = -30.0;
		column_locs[c1][1] = (c1+1)*(ROOM_LENGTH/(NUM_COLUMNS+1));
	}
	column_locs[c1][0] = -10.0;
	column_locs[c1][1] = ROOM_LENGTH-20;
	for (int c2=0; c2 < NUM_COLUMNS; c2++) {
		column_locs[NUM_COLUMNS+1+c2][0] = 30.0;
		column_locs[NUM_COLUMNS+1+c2][1] = (c2+1)*(ROOM_LENGTH/(NUM_COLUMNS+1));
	}
	column_locs[NUM_COLUMNS+1+c2][0] = 10.0;
	column_locs[NUM_COLUMNS+1+c2][1] = ROOM_LENGTH-20;


	CameraX = NeoX+(7*TORSO_HEIGHT*sin((yrot)*PI/180));
	CameraZ = NeoZ+(7*TORSO_HEIGHT*sin((90-yrot)*PI/180));

	curMouse.x = 0;
	curMouse.y = 0;

	while (1)
	{
		// Process All Messages
		while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) == TRUE)
		{
			if (GetMessage(&msg, NULL, 0, 0))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else
			{
				return TRUE;
			}
		}

		DrawGLScene();
		SwapBuffers(hDC);
		if (keys[VK_ESCAPE]) SendMessage(hWnd,WM_CLOSE,0,0);
		if (keys['L'] && !lp)
		{
			lp=TRUE;
			light=!light;
			if (!light)
			{
				glDisable(GL_LIGHTING);
			}
			else
			{
				glEnable(GL_LIGHTING);
			}
		}
		if (!keys['L'])
		{
			lp=FALSE;
		}
		if (keys['F'] && !fp)
		{
			fp=TRUE;
			filter+=1;
			if (filter>2)
			{
				filter=0;
			}
		}
		if (!keys['F'])
		{
			fp=FALSE;
		}

	/*
		if (!lastq && keys['Q']){
			movespeed=movespeed /3;
			lastq=1;
		}
		if (lastq && !keys['Q']){
			movespeed=movespeed*5;
			lastq=0;
		}
	*/
		for (int i=0;i<dudeArray[0]->currentstate->numbranches;i++) {
			if (((keys[dudeArray[0]->currentstate->branchchar[i]] && 
				(dudeArray[0]->currentstate->flags[i]==1)) ||
				(!keys[dudeArray[0]->currentstate->branchchar[i]] && 
				(dudeArray[0]->currentstate->flags[i]==3))) || 
				(keys[dudeArray[0]->currentstate->branchchar[i]] && 
				(dudeArray[0]->currentstate->flags[i]==2)) ||
				(!keys[dudeArray[0]->currentstate->branchchar[i]] && 
				(dudeArray[0]->currentstate->flags[i]==4)) ||
			(dudeArray[0]->currentstate->branchchar[i]=='0' && dudeArray[0]->done==1)){
				hashentry *temphasher;
				movefa *nextstate;

				dudeArray[0]->nextstate=dudeArray[0]->currentstate->branchpos[i];
				movehash->Find(dudeArray[0]->nextstate->startpos,&temphasher);
				dudeArray[0]->switchnow=1;
				for (int k=0;k<temphasher->movenumways;k++){
					for (int j=0;j<NUMROTS;j++){
						dudeArray[0]->waysArray[k][j]=temphasher->movewaysArray[k][j];
					}
				}
				dudeArray[0]->numways=temphasher->movenumways;
				if (dudeArray[0]->currentstate->flags[i]==1 ||
					dudeArray[0]->currentstate->flags[i]==3)
					dudeArray[0]->currentwaypoint=-1;
				dudeArray[0]->currentstate=dudeArray[0]->nextstate;
				breaknow=1;
				dudeArray[0]->done=0;
				break;
			}
		}	

		if (keys['P'])
		{
		fprintf(debugfile,"CurrentObjs\n");
			for (int j=0;j<dudeArray[0]->numways;j++){
				for (int i=0;i<NUMROTS;i++){
					fprintf(debugfile,"%d ",dudeArray[0]->waysArray[j][i]);
				}
				fprintf(debugfile,"\n");
			}
			fprintf(debugfile,"\ndone\n\n");
		}
		
	}
}
